package web

import (
	"fmt"
	"strings"
)

// 用来支持对路由树的操作
// 代表路由树（森林）
type router struct {
	// Beego Gin HTTP method 对应一棵树
	// GET 一颗，POST 一颗 ……

	// http method => 路由树根节点
	trees map[string]*node
}

// type tree struct {
// 	root *node
// }

func newRouter() router {
	return router{
		trees: map[string]*node{},
	}
}

// 加一些限制:
// path 必须以 / 开头, 不能以 / 结尾, 中间不能有连续的 //
func (r *router) addRoute(method string, path string, handleFunc HandleFunc) {
	if path == "" {
		panic("web: 路径不能为空字符串")
	}
	// 注册到路由树
	root, ok := r.trees[method]
	if !ok {
		// 说明还没有根节点
		root = &node{
			path: "/",
		}
		// 处理方法
		r.trees[method] = root
	}

	// 根节点特殊处理
	if path == "/" {
		if root.handler != nil {
			// 根节点重复注册
			panic("web: 路由冲突, 重复注册: [/]")
		}
		root.handler = handleFunc
		root.route = "/"
		return
	}

	// 开头不能没有 /
	if path[0] != '/' {
		panic("web: 路径必须以 / 开头")
	}

	// 结尾
	if path != "/" && path[len(path)-1] == '/' {
		panic("web: 路径不能以 / 结尾")
	}

	// 中间连续 //, 可以用string.contains("//")

	// /user/home -> 会被切成3段
	path = path[1:] // /user -> user
	// 切割这个 path
	segs := strings.Split(path, "/")
	for _, seg := range segs {
		if seg == "" {
			panic("web: 不能有连续的 /")
		}

		// 递归下去, 中途有节点不存在就创建
		child := root.childOrCreate(seg)
		// root向下(深)走
		root = child
	}

	// 前面root已经走到/user/home, 所以这里添加处理方法
	// 是对/user/home的处理
	if root.handler != nil {
		// 普通路由重复注册
		panic(fmt.Sprintf("web: 路由冲突, 重复注册: [/%s]", path))
	}
	root.handler = handleFunc
	root.route = path
}

// 路由查找
func (r *router) findRoute(method string, path string) (*matchInfo, bool) {
	// 沿着树深度查找下去
	root, ok := r.trees[method]
	if !ok {
		return nil, false
	}

	// 根节点
	if path == "/" {
		return &matchInfo{n: root}, true
	}

	// 把path前后的 / 去掉
	path = strings.Trim(path, "/")
	// 按照 / 切割
	segs := strings.Split(path, "/")
	var pathParams map[string]string
	for _, seg := range segs {
		child, paramChild, found := root.childOf(seg)
		if !found {
			return nil, false
		}
		//命中了路径参数
		if paramChild {
			if pathParams == nil {
				pathParams = make(map[string]string)
			}
			// path 是 :id 这种形式
			pathParams[child.path[1:]] = seg
		}
		root = child
	}
	// 表示有该节点，但是不一定有handler
	return &matchInfo{n: root, pathParams: pathParams}, true
	// return root, root.handler != nil
}

// 找到对应child
// 参数1：找到的子节点
// 参数2：找到的child是不是路径参数
// 参数3：是否找到
func (n *node) childOf(path string) (*node, bool, bool) {
	// 优先考虑静态匹配，匹配不上再考虑通配符
	if n.children == nil {
		if n.paramChild != nil {
			return n.paramChild, true, true
		}
		// return nil, false
		return n.starChild, false, n.starChild != nil
	}
	child, ok := n.children[path]
	if !ok {
		if n.paramChild != nil {
			return n.paramChild, true, true
		}
		return n.starChild, false, n.starChild != nil
	}
	return child, false, ok
}

func (n *node) childOrCreate(seg string) *node {
	if seg[0] == ':' {
		if n.starChild != nil {
			panic("web: 不允许同时注册路径参数和通配符路径, 已经注册通配符路径")
		}
		n.paramChild = &node{
			path: seg,
		}
		return n.paramChild
	}
	// 通配符
	if seg == "*" {
		if n.paramChild != nil {
			panic("web: 不允许同时注册路径参数和通配符路径, 已经注册路径参数")
		}
		n.starChild = &node{
			path: seg,
		}
		return n.starChild
	}
	if n.children == nil {
		n.children = map[string]*node{}
	}
	res, ok := n.children[seg]
	if !ok {
		// 新建
		res = &node{
			path: seg,
		}
		n.children[seg] = res
	}
	return res
}

type node struct {
	// /a/b/* -> /a/b/*
	route string
	// 当前路由 /a/* -> *
	path string
	// 静态匹配的节点
	// path => 子节点
	children map[string]*node
	// 通配符(/*)匹配的节点
	starChild *node
	// 路径参数
	paramChild *node
	// 需要一个代表用户注册的业务逻辑
	handler HandleFunc
}

// 将参数和节点一起返回
type matchInfo struct {
	n          *node
	pathParams map[string]string
}
